package at.ac.tuwien.prog.flash;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import at.ac.tuwien.prog.Util;

/**
 * 
 * @author christoph.pickl@student.tuwien.ac.at
 */
public class FlashManagerImpl implements IFlashManager {
	
	private static final Log LOG = LogFactory.getLog(FlashManagerImpl.class);
	
	public static final int BLOCK_SIZE = 512; // byte
	
	public static final int BLOCK_COUNT = 20; // arbitrary value
	
	private final File file;
	
	private final InitFlashWriter initFlashWriter;
	
	public FlashManagerImpl(final File file, final InitFlashWriter initFlashWriter) {
		this.file = file;
		this.initFlashWriter = initFlashWriter;
		
		LOG.info("Creating flash manager by file ["+file.getAbsolutePath()+"]");
		
		
		if(file.exists() == false) {
			LOG.debug("Creating new flash file.");
			
			// create file
			try {
				final boolean fileCreated = file.createNewFile();
				if(fileCreated == false) {
					throw new RuntimeException("Could not create file at ["+file.getAbsolutePath()+"]!");
				}
			} catch (IOException e) {
				throw new RuntimeException("Could not create flash file at ["+file.getAbsolutePath()+"]!");
			}
			
			// write content
			OutputStream output = null;
			try {
				output = new FileOutputStream(file);
				output.write(this.initFlashWriter.initialFlashData());
			} catch (IOException e) {
				throw new RuntimeException("Could not initialize flash file at ["+file.getAbsolutePath()+"]!");
			} finally {
				Util.close(output);
			}
			
		}
		
	}
	
	public byte[] readBlock(int blockNumber) throws IOException {
		LOG.info("readBlock(blockNumber="+blockNumber+")");
		if(blockNumber > BLOCK_COUNT) {
			throw new IllegalArgumentException("blockNumber ["+blockNumber+"] > BLOCK_COUNT ["+BLOCK_COUNT+"]");
		}
		
		RandomAccessFile input = null;
		try {
			input = new RandomAccessFile(this.file, "r");
			input.seek(blockNumber * BLOCK_SIZE);
			final byte[] result = read(input, BLOCK_SIZE);
			return result;
		} finally {
			Util.close(input);
		}
	}

	public void writeBlock(int blockNumber, byte[] blockData) {
		LOG.info("write(blockNumber="+blockNumber+")");
		// FIXME implement me
		if(blockData.length != 512) {
			// 128 ints * 4 byte = 512 byte
			throw new IllegalArgumentException("blockData.length ["+blockData.length+"] != 512");
		}

	}
	
	private static byte[] read(RandomAccessFile input, int length) throws IOException {
		final byte[] result = new byte[length];
		
		int inputTotalRead = 0;
		while(inputTotalRead != length) {
			final int inputRead = input.read(result, inputTotalRead, length - inputTotalRead);
			inputTotalRead += inputRead;
			
//			LOG.trace("read; inputTotalRead="+inputTotalRead);
			
			if(inputRead == -1 && inputTotalRead != length) {
				throw new RuntimeException("EOF reached! (maybe corrupt flash file? try deleting it)");
			}
//			LOG.trace("read(length="+length+") inputRead="+inputRead+"; inputTotalRead="+inputTotalRead);
		}
		
		return result;
	}

}
